﻿// --------------------------------------------------------------------------------------------------------------------- 
// Author : Charl Truter
// Creation Date : 2012-03-15
// Summary : This application can be used to load pages from a CSV, get their page rank and dump it back into a csv
// ---------------------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GooglePageRankChecker;
using System.IO;
using System.Threading;
using System.Configuration;

namespace CSVPagerankChecker
{
    class Program
    {
        #region Properties

        /// <summary>
        /// This property is used to store the page rank results
        /// </summary>
        private static Dictionary<string, int> Results = new Dictionary<string, int>();

        /// <summary>
        /// This property is used to store the amount of enqueued threads
        /// </summary>
        private static int RunningThreads = 0;

        #endregion Properties

        #region Methods

        /// <summary>
        /// Main executing method
        /// </summary>
        /// <param name="args">Arguments sent on startup</param>
        static void Main(string[] args)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings["PageRankCheckURL"]))
                {
                    throw new Exception("The PageRankCheckURL in the app.config is not properly defined!");
                }

                Console.WriteLine("To see an example of a valid CSV file to use for this application, please check the Example.csv file.\r\n");
                Console.WriteLine("Please enter the path to the csv containing the page urls to check.");
                Console.WriteLine("Enter it in the following format : C:\\Temp\\Pages.csv");
                string filePath = Console.ReadLine();

                // Check if the file exists
                if (!File.Exists(filePath))
                {
                    throw new Exception(string.Format("File {0} does not exist!", filePath));
                }

                // Compile a list of pages to check
                Console.WriteLine("\r\nCompiling a list of pages to check...");
                List<string> pagesToCheck = GetPagesToCheck(filePath);
                Console.WriteLine("{0} pages to check!", pagesToCheck.Count);
                                
                if (pagesToCheck.Count > 0)
                {
                    foreach (var page in pagesToCheck)
                    {
                        // Made this multithreaded as single-threaded it was only doing around 2 checks per second
                        Interlocked.Increment(ref RunningThreads);
                        ThreadPool.QueueUserWorkItem(new WaitCallback(GetPageRank), page);
                    }

                    // Wait till all the enqueued threads are done
                    while (RunningThreads > 0)
                    {
                        Console.WriteLine("{0} threads are still running! Waiting 5 seconds...", RunningThreads);
                        Thread.Sleep(5000);
                    }
                }
                else
                {
                    throw new Exception("The CSV file is empty!");
                }

                // Create the new file path where the results will be written to
                string newFilePath = string.Format("{0}_Processed.csv", filePath.Substring(0, filePath.IndexOf(".csv")));

                // Write the results
                WriteResults(newFilePath);

                Console.WriteLine("\r\nPage rank processing completed successfully!");
                Console.WriteLine("Press any key to continue...");
            }
            catch (Exception ex)
            {
                Console.WriteLine("\r\nAn error has occured!");
                Console.WriteLine("Error information : {0}", ex.Message.ToString());
                Console.WriteLine("Page rank processing has failed!");
            }

            Console.ReadKey();
        }

        /// <summary>
        /// This methods is used to read data from a CSV file and create a list unique pages to check
        /// </summary>
        /// <param name="filePath">Path to the CSV file containing the list of pages</param>
        /// <returns>List of pages to be checked</returns>
        private static List<string> GetPagesToCheck(string filePath)
        {
            List<string> returnList = new List<string>();

            using (var reader = File.OpenText(filePath))
            {
                string placeholder = string.Empty;
                while ((placeholder = reader.ReadLine()) != null)
                {
                    if (!returnList.Contains(placeholder.Trim()))
                    {
                        returnList.Add(placeholder.Trim());
                    }
                }
            }

            return returnList;
        }

        /// <summary>
        /// This method is used to check the page rank for a specific page.
        /// Once the page rank is determined, the result is added to the Results dictionary.
        /// If an error occurs, the page rank is left at -1.
        /// </summary>
        /// <param name="obj">State object</param>
        private static void GetPageRank(object obj)
        {
            string page = obj.ToString();

            // Default the page rank to -1
            int pageRank = -1;
            Console.WriteLine("Checking page rank for {0}", page);
            try
            {
                GooglePageRank rankChecker = new GooglePageRank();
                pageRank = rankChecker.GetPageRank(page);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error while trying to get page rank for {0}! Error : {1}", page, ex.Message.ToString());
            }

            // Insert the result in the Results dictionary
            lock (Results)
            {
                Results.Add(page, pageRank);
            }

            // Decrement the amount of currently running threads
            Interlocked.Decrement(ref RunningThreads);
        }

        /// <summary>
        /// This method is used to write the page rank results to a CSV.
        /// </summary>
        /// <param name="filePath">Path the CSV file should be written to</param>
        private static void WriteResults(string filePath)
        {
            try
            {
                Console.WriteLine("\r\nWriting results to file {0}", filePath);
                using (StreamWriter writer = new StreamWriter(filePath))
                {
                    foreach (var result in Results)
                    {
                        writer.WriteLine(string.Format("{0},{1}", result.Key, result.Value));
                    }
                }
                Console.WriteLine("Results written successfully!");
            }
            catch (Exception ex)
            {
                throw new Exception(string.Format("Error in Write Results! Message : {0}", ex.Message.ToString()));
            }
        }

        #endregion Methods
    }
}
